package com.smf.lsc.rest.crypt.filter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;

import com.smf.lsc.rest.crypt.util.AesEncryptUtils;
import com.smf.lsc.rest.crypt.util.ExcludedURIUtil;
import com.smf.lsc.rest.crypt.wrapper.ParaWrapperRequest;
import com.smf.lsc.rest.crypt.wrapper.WrapperRequest;
import com.smf.lsc.rest.crypt.wrapper.WrapperResponse;

import jdk.nashorn.internal.runtime.GlobalConstants;
import lombok.extern.slf4j.Slf4j;

/**
 * @author ln
 * rest加解密过滤器
 */
@Slf4j
public class RestCryptFilter implements Filter {

	// 加密模式--exclud白名单排除模式（默认全加密，排除不需要的url） --includ黑名单模式（默认不加密，包含需要加密的url
	@Value("${lsc.restCrypt.type:EXCLUD}")
	private String cryptType;
    
	@Value("${lsc.restCrypt.cludedURI}")
	private String cludedURI;

	// 加密方法--get/post/all
	@Value("${lsc.restCrypt.method:All}")
	private String cryptMethod;

	@Value("${lsc.restCrypt.responseCryptEnable:false}")
	private boolean responseCryptEnable;

	private static final String METHOD_NAME = "ALL";
	private static final String METHOD_GET = "GET";
	private static final String METHOD_POST = "POST";
	private static final String TYPE_EXCLUD = "EXCLUD";
	private static final String TYPE_INCLUD = "INCLUD";
	
	/** 用于识别出不需要拦截的URI */
    private static ExcludedURIUtil cludedURIUtil;

	@Override
	public void init(FilterConfig filterConfig) {
		if(!isEmpty(cludedURI))
		{
			String[] excludedURLPaths = cludedURI.split(",");
			cludedURIUtil = ExcludedURIUtil.getInstance(excludedURLPaths);
		}
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
		try {
			
			WrapperResponse wrapResponse = new WrapperResponse((HttpServletResponse) response);
			
			HttpServletRequest httpRequest = (HttpServletRequest) request;
			String requestURI = httpRequest.getRequestURI();
			if(!isEmpty(cludedURI))
			{
				//白名单模式--排除url不加密
				if (cryptType.toUpperCase().equals(TYPE_EXCLUD)) {
					if(cludedURIUtil.match(requestURI))
					{
						filterChain.doFilter(request, response);
						return;
					}
				} 
				//黑名单模式--包含的url才加密
				else if (cryptType.toUpperCase().equals(TYPE_INCLUD)) {
					if(!cludedURIUtil.match(requestURI))
					{
						filterChain.doFilter(request, response);
						return;
					}
				}
			}
    
			//对请求get/post参数分开加密控制
			if (cryptMethod.toUpperCase().equals(httpRequest.getMethod().toUpperCase())
					|| cryptMethod.toUpperCase().equals(METHOD_NAME)) {
				if (METHOD_GET.equals(httpRequest.getMethod().toUpperCase())) {
					Map<String, Object> newParameterMap = new HashMap();
					Map<String, String[]> maps = request.getParameterMap();
					for (Map.Entry<String, String[]> params : maps.entrySet()) {
						String key = params.getKey();
						String[] values = params.getValue();
						String value = "";
						if (!isEmpty(values[0])) {
							value = AesEncryptUtils.decrypt(values[0]);
						}
						newParameterMap.put(key, value);
					}
					if(newParameterMap.size()>0)
					{
						ParaWrapperRequest paraWrapRequest = new ParaWrapperRequest((HttpServletRequest) request, newParameterMap);
						filterChain.doFilter(paraWrapRequest, wrapResponse);
					}
					else
					{
						filterChain.doFilter(request, wrapResponse);
					}
				} else if (METHOD_POST.equals(httpRequest.getMethod().toUpperCase())) {
					String requestBody = getRequestBody((HttpServletRequest) request);
					String decRequestBody = AesEncryptUtils.decrypt(requestBody);
					WrapperRequest wrapRequest = new WrapperRequest((HttpServletRequest) request, decRequestBody);
					filterChain.doFilter(wrapRequest, wrapResponse);
				}
			} else {
				filterChain.doFilter(request, wrapResponse);
			}

			//对返回内容加密控制
			if (responseCryptEnable) {
				byte[] data = wrapResponse.getResponseData();
				String encResponseBody = AesEncryptUtils.encrypt(new String(data, StandardCharsets.UTF_8));
				writeResponse(response, encResponseBody);
			}
			else
			{
				byte[] data = wrapResponse.getResponseData();
				String encResponseBody = new String(data, StandardCharsets.UTF_8);
				writeResponse(response, encResponseBody);
			}
			
		} catch (Exception e) {
			log.error("RestCryptFilter filed,filed info:{}", e);
            //抛出异todo
			HttpServletResponse httpResponse = (HttpServletResponse)response;
			httpResponse.setStatus(500);
			httpResponse.setCharacterEncoding("UTF-8");
			httpResponse.setContentType("application/json; charset=utf-8");
	        PrintWriter out = null;
	        try {
	            out = response.getWriter();
	            out.append(e.getMessage());
	        } catch (IOException ex) {
	        	log.error("RestCryptFilter filed,filed info:{}", ex);
	        } finally {
	            if (out != null) {
	                out.close();
	            }
	        }
		}

	}

	private void writeResponse(ServletResponse response, String responseString) throws IOException {
		PrintWriter out = response.getWriter();
		out.print(responseString);
		out.flush();
		out.close();
	}

	private String getRequestBody(HttpServletRequest req) {
		try {
			BufferedReader reader = req.getReader();
			StringBuffer sb = new StringBuffer();
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
			String json = sb.toString();
			return json;
		} catch (IOException e) {
			log.error("验签时请求体读取失败", e);
		}
		return "";
	}

	@Override
	public void destroy() {

	}

	private boolean isEmpty(String str) {
		boolean bool = false;
		if (str == null || str.isEmpty() || "".equals(str)) {
			bool = true;
		}
		return bool;
	}
}
